﻿using System;
//        
//****************************************************************************************
//*This is the main function. In Mul function you will find detailed explanation. No need*
//*to explain others because they are pretty much the same but with little changes.      *
//****************************************************************************************

namespace NumericalMethod
{
    class VParser
    {
        string[] constants = new string[] { "e","pi" };
        
        public double Parse(string Exp)
        {
            Exp = Exp.Trim();   //Remove Spaces
            //Exp = Exp.ToUpper();
            foreach (string c in constants) if (Exp.Contains(c)) Exp = Constant(Exp);
            Exp = RemPlusMinus(Exp); //Change +- to - and -- to +
            IsValid (Exp);          //Invalid equations trapping

            //The If's are to make sure the code is run only if neccessary
            if(Exp.IndexOf("[")>=0) Exp = CSTA(Exp);          //Eliminate Cos/Sin/Tan/Atn from equation.
            if(Exp.IndexOf("|")>=0) Exp = Abso(Exp);          //Eliminate Absolute signs from equation.
            if(Exp.IndexOf('(')>=0) Exp = Parentheses(Exp);   //Eliminate Parentheses from equation.
            if(Exp.IndexOf("^")>=0) Exp = Power(Exp);         //Eliminate Exponentials from equation.
            if(Exp.IndexOf("*")>=0) Exp = Mul(Exp);           //Eliminate Multiplications from equation.
            if(Exp.IndexOf("/")>=0) Exp = Div(Exp) ;          //Eliminate Divisions from equation.
            Exp = RemPlusMinus(Exp); 
            if(Exp.IndexOf("-")>= 0) Exp = Subt(Exp);          //Eliminate Subtraction from equation.
            if(Exp.IndexOf("+")>= 0) Exp = Add(Exp);           //Eliminate Addition from equation.
            if (Exp == "") return 0.0;
            return Double.Parse(Exp);
        }

        private string Constant(string Exp)
        {
            Exp = Exp.Replace("e", Math.E.ToString());
            Exp = Exp.Replace("pi", Math.PI.ToString());
            return Exp;
        }

        private string Parentheses(string Exp)
        {
            int Place;
            int Y;
            int X;
            string BeforeStr;
            string MiddleStr="";
            string NewExp;
            Exp = RemPlusMinus(Exp);
            Place = Exp.IndexOf(")");
            if( Place >= 0 )
            {
                Y = 0;
                X = Place;
                do //loop to find the inside of the parentheses
                {
                    Y = Y + 1;
                    X = X - 1;
                    if( X == 0 ) break;// Then Exit Do 'incase we got to the start of the string
                    if( Mid(Exp, X, 1) != ")" )
                        MiddleStr = Mid(Exp, X, Y);
                }while(Mid(Exp, X, 1) != "(");
                
                BeforeStr = "";//if it's the beginning of the string - there is nothing before.
                if( X > 0 )
                    BeforeStr = Mid(Exp, 1, X - 1);
                NewExp = BeforeStr + Parse(MiddleStr) + Mid(Exp, Place + 1);
                if (Exp.IndexOf("(", 1) > 0)
                    NewExp = Parentheses(NewExp); //Recourse incase there are more parentheses
                return NewExp;
            }
            return Exp;
        }

        private string Abso(string Exp)
        {
            int First;
            int Second;
            string NewExp;
            First = Exp.IndexOf("|");

            if(First == 0) return Exp;
            Second = Exp.IndexOf("|");
            NewExp = Mid(Exp, First + 1, Second - First - 1);
            NewExp = Parse(NewExp).ToString();
            return Mid(Exp, 1, First - 1) + Math.Abs(double.Parse(NewExp)) + Mid(Exp, Second + 1);
        }

        private string Power(string Exp)
        {
            int Place;    //Where the sign is found
            int X;        //Travel backward and forward on the string
            int Y = 0;        //Counter
            string Before="";    //What number goes before the sign
            string After = "";     //What number goes after the sign
            string NewExp;
            string BeforeStr; //Used to save the text that goes before our calculation
            Exp = RemPlusMinus(Exp); //Make sure previous calculation didn//t leave +- or --
            Place = Exp.IndexOf("^");
            if (Place > 0)
            {
                X = Place;
                Before = FindBeforeNumber(Exp, Place, ref X, "Asss");
                BeforeStr = Exp.Substring(0, X);
                Y = 0;
                X = Place;
                After = FindAfterNumber(Exp, Place, ref X, Y);
                NewExp = BeforeStr + (Math.Pow(Double.Parse(Before), Double.Parse(After))) + Exp.Substring(X);
                if (NewExp.IndexOf("^") > 0)
                   NewExp = Power(NewExp); //Recourse incase there are ^'s left
                return NewExp;
            }
            return Exp;
        }

        private string FindBeforeNumber(string Exp, int Place, ref int X, string op)
        {
            int Y = 0;
            X = Place;
            string Before = "";
            bool Plus,Minus,Multi,Dev,Flag=false;
            do //loop backwards on the string to find out the number before the sign
            {
                Y++;
                X--;
                if (X < 0) break; //incase we got to the start of the string
                int inv=1;
                if (X < 1)
                    inv = 0;
                Plus = op != "+" ? (Mid(Exp, X, 1) == "+"
                    && Y != 1 && Mid(Exp, X - inv, 1) != "E") : false;
                Minus = op != "-" ? (Mid(Exp, X, 1) == "-" 
                    && Y != 1 && Mid(Exp, X-inv, 1)!="E" 
                    && X != 0 ) : false;
                Multi = op != "*" ? (Mid(Exp, X, 1) == "*") : false;
                Dev = op != "/" ? (Mid(Exp, X, 1) == "/") : false;
                if (!Plus && !Minus && !Dev && !Multi)
                {
                   //no good, return if E
                   /*if (Exp.Substring(X, Y).IndexOf("E")>0)
                   {
                      X = 0;
                      return Exp;
                   }*/
                   Before = Exp.Substring(X, Y);
                }
                else
                   Flag = true;
            } while (X>=0&&!Flag);
            //recover the X
            X++;
            return Before;
        }
        private string Mul(string Exp)
        {
            int Place;    //Where the sign is found
            int X;        //Travel backward and forward on the string
            int Y=0;        //Counter
            string Before="";    //What number goes before the sign
            string After = "";     //What number goes after the sign
            string NewExp;
            string BeforeStr; //Used to save the text that goes before our calculation
            Exp = RemPlusMinus(Exp); //Make sure previous calculation didn//t leave +- or --
            if(Exp=="")               //Incase there//s nothing to do, make sure the function returns correct information
                return Exp;
            Place = Exp.IndexOf("*",1);
            if (Place > 0)
            {
                X = Place;
                Before = FindBeforeNumber(Exp, Place, ref X, "*");
                BeforeStr = Exp.Substring(0, X);

                Y = 0;
                X = Place;
                After = FindAfterNumber(Exp, Place, ref X, Y);
                NewExp = BeforeStr + (Double.Parse(Before) * Double.Parse(After)) + Exp.Substring(X);
                if (NewExp.IndexOf("*", 1) > 0)
                    NewExp = Mul(NewExp); //Recourse incase there are /s left
                return NewExp;
            }
            return Exp;
        }

        private string Div(string Exp)
        {
            int Place;    //Where the sign is found
            int X;        //Travel backward and forward on the string
            int Y=0;        //Counter
            string Before="";    //What number goes before the sign
            string After = "";     //What number goes after the sign
            string NewExp;
            string BeforeStr; //Used to save the text that goes before our calculation
            Exp = RemPlusMinus(Exp); //Make sure previous calculation didn//t leave +- or --
            if (Exp == "")               //Incase there//s nothing to do, make sure the function returns correct information
                return Exp;
            Place = Exp.IndexOf("/",1);
            if (Place > 0)
            {
                X = Place;
                Before = FindBeforeNumber(Exp, Place, ref X, "/");
                BeforeStr = Exp.Substring(0, X);
                Y = 0;
                X = Place;
                After = FindAfterNumber(Exp, Place, ref X, Y); 
                try
                {
                    NewExp = BeforeStr + (Double.Parse(Before) / Double.Parse(After)) + Exp.Substring(X);
                }
                catch (System.DivideByZeroException)
                {
                    return "Imposible";
                }
                if (NewExp.IndexOf("/") > 0)
                    NewExp = Div(NewExp); //Recourse incase there are +s left
                return NewExp;
            }
            return Exp;
        }

        private string Subt(string Exp)
        {
            int Place;    //Where the sign is found
            int X;        //Travel backward and forward on the string
            int Y=0;        //Counter
            string Before="";    //What number goes before the sign
            string After = "";     //What number goes after the sign
            string NewExp;
            string BeforeStr; //Used to save the text that goes before our calculation
            Exp = RemPlusMinus(Exp); //Make sure previous calculation didn//t leave +- or --
            if (Exp == "")               //Incase there//s nothing to do, make sure the function returns correct information
                return Exp;
            Place = Exp.IndexOf("-",1);
            while (Place > 0&&Exp.Substring(Place - 1, 1) == "E")
                Place = Exp.IndexOf("-",Place+1);
            if (Place > 0)
            {
                Y = 0;
                X = Place;
                X = Place;
                Before = FindBeforeNumber(Exp, Place, ref X, "-");
                BeforeStr = Exp.Substring(0, X);
                Y = 0;
                X = Place;
                After = FindAfterNumber(Exp, Place, ref X, Y);

                NewExp = BeforeStr + (Double.Parse(Before) - Double.Parse(After)) + Exp.Substring(X);
                if (NewExp.IndexOf("-", 1) > 0 && Exp.Substring(Place - 1,1) != "E")
                    NewExp = Subt(NewExp); //Recourse incase there are -s left
                return NewExp;
            }
            return Exp;
        }

        private string FindAfterNumber(string Exp, int Place, ref int X, int Y)
        {
            string Word="";
            string temp;
            do
            {
                Y++;
                X++;
                if (X>= Exp.Length)
                    break;
                temp = Exp.Substring(X, 1);
                if ((temp != "+" || Y == 1 || Exp.Substring(X - 1, 1) == "E") 
                    && (temp != "-" || Y == 1 || Exp.Substring(X-1, 1) == "E") 
                    && (temp != "*") 
                    && (temp != "/"))
                {
                    Word = Exp.Substring(Place + 1, Y);
                }
                
            }
            while ((temp != "+" || Y == 1 || Exp.Substring(X - 1, 1) == "E") 
                && (temp != "-" || Y == 1 || Exp.Substring(X - 1, 1) == "E") 
                && (temp != "*") 
                && (temp != "/"));
            return Word;
        }

        private string Add(string Exp)
        {
            int Place;    //Where the sign is found
            int X;        //Travel backward and forward on the string
            int Y=0;        //Counter
            string Before="";    //What number goes before the sign
            string After = "";     //What number goes after the sign
            string NewExp="";
            string BeforeStr; //Used to save the text that goes before our calculation
            Exp = RemPlusMinus(Exp); //Make sure previous calculation didn//t leave +- or --
            if (Exp == "")               //Incase there//s nothing to do, make sure the function returns correct information
                return Exp;
            Place = Exp.IndexOf("+",1);
            while (Place > 0 && Exp.Substring(Place - 1, 1) == "E")
                Place = Exp.IndexOf("-", Place + 1);
            if (Place > 0)
            {
                X = Place;
                Before = FindBeforeNumber(Exp, Place, ref X, "+");
                BeforeStr = Exp.Substring(0, X);
                Y = 0;
                X = Place;
                After = FindAfterNumber(Exp, Place, ref X, Y);
                try
                {
                    NewExp = BeforeStr + (Double.Parse(Before) + Double.Parse(After)) + Exp.Substring(X);
                    if (NewExp.IndexOf("+", 1) > 0 && Exp.Substring(Place - 1,1) != "E")
                        NewExp = Add(NewExp); //Recourse incase there are +s left
                }
                catch (System.FormatException Ex)
                {
                    System.Console.Write(Ex.Message);
                }
                return NewExp;
            }
            return Exp;
        }

        private string CSTA(string Exp)
        {
            int Place, X,Y;
            string BeforeStr="";
            string MiddleStr="";
            string NewExp="";
            string XType ="";
            Exp = RemPlusMinus(Exp); //Make sure previous calculation didn//t leave +- or --
            if (Exp == "")               //Incase there's nothing to do, make sure the function returns correct information
                return Exp;
            Place = Exp.IndexOf("]",1);
            if( Place > 0 )
            {
                Y = 0;
                X = Place;
                do
                { //loop to find the inside of the brackets
                    Y++;
                    X--;
                    if( X < 0) break; //incase we got to the start of the string
                    if( Exp.Substring( X, 1) != "[" )
                        MiddleStr = Exp.Substring( X, Y);
                }while (Exp.Substring( X, 1) != "[");
                BeforeStr = "";// 'if it's the beginning of the string - there is nothing before.
                if (X > 0)
                {
                    BeforeStr = Exp.Substring(0, X - 3);
                    XType = Exp.Substring(X - 3, 3);
                }
                switch( XType )
                {
                    case "cos":
                    NewExp = BeforeStr + Math.Cos(Parse(MiddleStr)) + Exp.Substring(Place + 1);
                    break;
                    case "sin":
                    NewExp = BeforeStr + Math.Sin(Parse(MiddleStr)) + Exp.Substring(Place + 1);
                    break;
                    case "tan":
                    NewExp = BeforeStr + Math.Tan(Parse(MiddleStr)) + Exp.Substring(Place + 1);
                    break;
                    case "atan":
                    case "atn":
                    NewExp = BeforeStr + Math.Atan(Parse(MiddleStr)) + Exp.Substring(Place + 1);
                    break;
                    case "lgn":
                    NewExp = BeforeStr + Math.Log(Parse(MiddleStr)) + Exp.Substring(Place + 1);
                    break;
                }
                if (NewExp.IndexOf("]", 1) > 0)
                    NewExp = CSTA(NewExp); //Recourse incase there are more brackets
            }

            return NewExp;
        }

        private string RemPlusMinus(string Exp)
        {
            int Place;
            string NewExp="";
            Place = Exp.IndexOf( "+-");
            if(Place >= 0)
            {
                NewExp = Left(Exp, Place - 1) + "-" + Right(Exp, Exp.Length - Place - 2);
                if(NewExp.IndexOf( "+-") > 0 )
                    NewExp = RemPlusMinus(NewExp);
                return NewExp;
            }
            Place = Exp.IndexOf("-+");
            if (Place >= 0)
            {
                NewExp = Left(Exp, Place - 1) + "-" + Right(Exp, Exp.Length - Place - 2);
                if (NewExp.IndexOf("-+") > 0)
                    NewExp = RemPlusMinus(NewExp);
                return NewExp;
            }
            Place = Exp.IndexOf("--");
            if (Place >= 0)
            {
                NewExp = Left(Exp, Place) + "+" + Right(Exp, Exp.Length - Place -2);
                if (NewExp.IndexOf("--") > 0)
                    NewExp = RemPlusMinus(NewExp);
                return NewExp;
            }
            return Exp;
        }

        private void IsValid(string Exp)
        {
            string Temp="";
            int X;
            X = 0;
            do{
                X++;
                if(Exp.Length<=1)
                   continue;
                if (X >= Exp.Length-1)
                   break;
                switch(Mid(Exp, X, 1))
                {
                    case "[": case "]": case "(": case ")": 
                    case "*": case  "/": case "+": case "-": case "^":

                    break;
                    case "|":
                    if(Mid(Exp, X, 2) == "||")
                    {
                        throw new Exception("Wierdo");
                    }
                    break;
                    default:
                        double TempD;
                        if (!Double.TryParse(Mid(Exp, X, 1), out TempD) && !(Mid(Exp, X, 1)==".") && !(Mid(Exp, X, 1)=="E"))
                        {
                            if (X == 1)
                            {
                                Temp = Mid(Exp, X - 1, 3);
                                if ((Temp != "tan") && (Temp != "cos") && (Temp != "sin") && (Temp != "atn") && Temp != "lgn")
                                    throw new Exception("Specified Text Sequence Not Allowed");
                            }
                            else if (X == 0)
                            {
                                Temp = Mid(Exp, X, 3);
                                if ((Temp != "tan") && (Temp != "cos") && (Temp != "sin") && (Temp != "atn") && Temp != "lgn")
                                    throw new Exception("Specified Text Sequence Not Allowed");
                            }
                            else if (X > 2)
                            {
                                Temp = Mid(Exp, X - 3, 6);
                                if ((Temp.IndexOf("cos[", 1) == 0) 
                                    && (Temp.IndexOf("sin[", 1) == 0) 
                                    && (Temp.IndexOf("tan[", 1) == 0)
                                    && (Temp.IndexOf("atn[", 1) == 0)
                                    && (Temp.IndexOf("lgn[", 1) == 0)
                                    )
                                    throw new Exception("Specified Text Sequence Not Allowed");
                            }
                        }
                        else
                        {
                            Temp = Mid(Exp, X, 2);
                            if ((Temp.IndexOf("[", 1) > 0) || (Temp.IndexOf("(", 1) > 0) || (Temp.IndexOf("s", 1) > 0) || (Temp.IndexOf("c", 1) > 0) || (Temp.IndexOf("t", 1) > 0) || (Temp.IndexOf("a", 1) > 0))
                                throw new Exception("Specified Text Sequence Not Allowed");
                         
                        }
                        break;
               }
            }while( X <= Exp.Length );
        }
        public string Left(string param, int length)
        {
            //we start at 0 since we want to get the characters starting from the
            //left and with the specified lenght and assign it to a variable
            if (length < 0)
                return "";
            string result = param.Substring(0, length);
            //return the result of the operation
            return result;
        } 
        public string Right(string param, int length)
        {
            //start at the index based on the lenght of the sting minus
            //the specified lenght and assign it a variable
            string result = param.Substring(param.Length - length, length);
            //return the result of the operation
            return result;
        }

        public string Mid(string param,int startIndex, int length)
        {
            //start at the specified index in the string ang get N number of
            //characters depending on the lenght and assign it to a variable
            string result = param.Substring(startIndex, length);
            //return the result of the operation
            return result;
        }

        public string Mid(string param,int startIndex)
        {
            //start at the specified index and return all characters after it
            //and assign it to a variable
            string result = param.Substring(startIndex);
            //return the result of the operation
            return result;
        }

    }
}
